package org.ns.learn.db.autoconf;

import com.alibaba.fastjson.JSON;
import com.zaxxer.hikari.HikariConfig;
import org.apache.commons.lang.StringUtils;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;

import java.nio.charset.Charset;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;

public class DBZookeeperConfigService {
    private String registerServerAddress;
    private ZooKeeper zk;
    private Map<String, Boolean> listenerConfigNames = new ConcurrentHashMap<>();
    private Map<String, DBSourceWrapper> dbSourceWrapperMap = new ConcurrentHashMap<>();
    private DefaultListableBeanFactory beanFactory;
    public DBZookeeperConfigService(String registerServerAddress, ConfigurableListableBeanFactory configurableListableBeanFactory) throws Exception {
        this.registerServerAddress = registerServerAddress;
        this.beanFactory = (DefaultListableBeanFactory)configurableListableBeanFactory;

        CountDownLatch connectAwait = new CountDownLatch(1);
        zk = new ZooKeeper(registerServerAddress, 3000, new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {
                if (watchedEvent.getState() == Event.KeeperState.SyncConnected) {
                    connectAwait.countDown();
                }
            }
        });
        connectAwait.await();
    }


    public void loaderConfig() {
        try {
            String dbconfigRoot = "/dbautoconf";
            Stat stat = zk.exists(dbconfigRoot, new Watcher() {
                @Override
                public void process(WatchedEvent event) {
                    if (event.getType() == Event.EventType.NodeCreated) {
                        loaderConfig();
                    }
                }
            });
            if (stat == null) {
                return;
            }
            System.out.println("节点OK！");

            List<String> dataSourceNames = zk.getChildren(dbconfigRoot, new Watcher() {
                @Override
                public void process(WatchedEvent event) {
                    if (event.getType() == Event.EventType.NodeChildrenChanged) {
                        loaderConfig();
                    }
                }
            });
            for (String dataSourceName : dataSourceNames) {
                listenerConfig(dataSourceName, false);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void listenerConfig(String configName, boolean loop) {
        try {
            if (!loop && listenerConfigNames.put(configName, true) != null) {
                return;
            }
//            System.out.println("name="+configName);
            String dataSourceConfigPath = "/dbautoconf/" + configName;
            Stat stat = zk.exists(dataSourceConfigPath, false);
            if (stat != null) {
                byte[] data = zk.getData(dataSourceConfigPath, new Watcher() {
                    @Override
                    public void process(WatchedEvent event) {
                        listenerConfig(configName, true);
                    }
                }, stat);

                String json = new String(data, Charset.forName("UTF-8"));
                System.out.printf("dbname:%s, data:%s", configName,json);
                if (StringUtils.isNotBlank(json)) {
                    json = json.trim();
                    if (json.startsWith("{") && json.endsWith("}")) {
                        HikariConfig hikariConfig = JSON.parseObject(json, HikariConfig.class);
                        configDataSource(configName, hikariConfig);
                    }
                }


            } else {
                listenerConfigNames.remove(configName);
                DBSourceWrapper dbSourceWrapper = dbSourceWrapperMap.remove(configName);
                if (dbSourceWrapper != null) {
                    dbSourceWrapper.close();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void configDataSource(String dataSourceName, HikariConfig hikariConfig) {

        DBSourceWrapper dbSourceWrapper = this.dbSourceWrapperMap.get(dataSourceName);
        if (dbSourceWrapper != null) {
            System.out.println("重置数据源: " + dataSourceName);
            dbSourceWrapper.restart(hikariConfig);
        } else {
            System.out.println("新配置数据源: " + dataSourceName);
            dbSourceWrapper = new DBSourceWrapper(hikariConfig);
            this.dbSourceWrapperMap.put(dataSourceName, dbSourceWrapper);
            BeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(DBSourceConfig.class)
                    .addConstructorArgValue(dbSourceWrapper)
                    .setLazyInit(true)
                    .getBeanDefinition();
            beanFactory.registerBeanDefinition(dataSourceName, beanDefinition);
        }
        System.out.println(JSON.toJSONString(hikariConfig, true));
    }
}
